package ar.com.altaagenda.spiders;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.htmlparser.Parser;
import org.htmlparser.tags.Div;
import org.htmlparser.tags.Span;
import org.htmlparser.tags.TableColumn;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.SimpleNodeIterator;

import ar.com.altaagenda.Result;
import ar.com.altaagenda.utils.ElementAttributeFilter;
import ar.com.altaagenda.utils.HttpExecutor;

public class CVSpider extends SheSpider {

	private static final String LUGAR = "Cafe Vinilo";
	private static final String BARRIO = "Palermo";
	private static final String MAIL = "info@cafevinilo.com";
	private static final String DIRECCION = "Gorriti 3780";
	private static final String WEB = "www.cafevinilo.com.ar";
	private static final String TEL = "4866 6510";
	private static String CHARSET = "UTF-8";
	private String domain = "http://www.cafevinilo.com.ar";
	private Pattern firstLinePattern = Pattern
			.compile("[A-Z][a-záé]+\\s\\d\\d?\\s-\\s\\d\\d?\\shs");
	private Pattern dayPattern = Pattern.compile("\\d\\d?\\s-");
	private Pattern hourPattern = Pattern.compile("(\\d\\d?)");
	private Pattern artistPattern = Pattern.compile("<\\s*strong\\s*>(.*?)<\\s*/\\s*strong>");

	public CVSpider(Calendar startDate, Calendar endDate) {
		super(startDate, endDate);
	}

	@Override
	public List<Result> getResults() {
		List<Result> results = new ArrayList<>();
		String url = this.generateUrl();

		System.out.println("Buscando resultados en: " + this.getSiteName());
		try {
			String htmlBody = new HttpExecutor(CHARSET).execute(url);

			Parser parser = new Parser();
			parser.setInputHTML(htmlBody);

			NodeList list = parser
					.extractAllNodesThatMatch(new ElementAttributeFilter(
							Span.class, "style", "color:#c40"));
			SimpleNodeIterator divs = list.elements();

			int mesActual = this.decodeMes(this.removeTagsAndEnters(divs
					.nextNode().toHtml()));
			int anioActual = this.calculateAnio(mesActual, this.startDate,
					this.endDate);

			if (anioActual != 0) {
				parser.setInputHTML(htmlBody);
				list = parser
						.extractAllNodesThatMatch(new ElementAttributeFilter(
								Div.class, "id", "mes-actual-cont"));
				divs = list.elements();

				String divMesActual = divs.nextNode().toHtml();

				results.addAll(this
						.process(divMesActual, mesActual, anioActual));
			}
			
			parser.setInputHTML(htmlBody);

			list = parser
					.extractAllNodesThatMatch(new ElementAttributeFilter(
							Span.class, "style", "color:#000"));
			divs = list.elements();

			int mesSig = this.decodeMes(this.removeTagsAndEnters(divs
					.nextNode().toHtml()));
			int anioSig = this.calculateAnio(mesSig, this.startDate,
					this.endDate);

			if (anioSig != 0) {
				parser.setInputHTML(htmlBody);
				list = parser
						.extractAllNodesThatMatch(new ElementAttributeFilter(
								Div.class, "id", "mes-sig-cont"));
				divs = list.elements();

				String divMesSig = divs.nextNode().toHtml();

				results.addAll(this
						.process(divMesSig, mesSig, anioSig));
			}
		} catch (Exception ex) {
			System.out.println("Exception caught trying to crawl this url: "
					+ url);
			ex.printStackTrace();
		}
		return results;
	}

	private List<Result> process(String resultsHtml, int mes, int anio)
			throws Exception {
		List<Result> results = new ArrayList<>();

		Parser parser = new Parser();
		parser.setInputHTML(resultsHtml);
		SimpleNodeIterator iterator = parser.extractAllNodesThatMatch(
				new ElementAttributeFilter(TableColumn.class, "class",
						"prog-mes")).elements();

		while (iterator.hasMoreNodes()) {
			String[] events = this.removeSpecificTags(iterator.nextNode()
					.toHtml(), "td").replaceAll("\n", "").split("</p>\\s*<p>");
			for(int i=0; i < events.length; i++){
				String[] eventItems = this.removeSpecificTags(events[i], "p").split("<\\s*br\\s*\\/>");
				String[] horarios = null;
				String nombreArtista = null;
				String nombreEvento = null;
				String fecha = null;
				
				for (String item : eventItems) {
					item = item.trim().replaceAll("\n", "");
					
					if (item.isEmpty())
						continue;
					
					if (this.isNewResult(item)) {
						this.addResults(results, nombreEvento, nombreArtista,
								fecha, horarios);
						fecha = this.extractFecha(item, mes, anio);
						horarios = this.extractHorarios(item);
						nombreArtista = null;
						nombreEvento = null;
					} else {
						if(nombreArtista == null){
							nombreArtista = this.extractArtista(item);
							nombreEvento = this.removeSpecificTags(item,"strong");
						} else {
							nombreEvento += item;
						}
					}
				}
				this.addResults(results, nombreEvento, nombreArtista, fecha,
						horarios);
			}
		}
		return results;
	}

	private String extractArtista(String item){
		Matcher matcher = this.artistPattern.matcher(item);
		
		if(matcher.find()){
			return matcher.group(1);
		}
		
		return "NO ENCONTRO";
	}
	
	private String[] extractHorarios(String item){
		int index = item.indexOf("-");
		int indexPesos = item.indexOf("$");
		Matcher matcher = this.hourPattern.matcher(item.substring(index, indexPesos > 0 ? indexPesos : item.length()));
		
		ArrayList<String> hours = new ArrayList<String>();
		while(matcher.find()){
			hours.add(matcher.group() + ":00");
		}
		return hours.toArray(new String[hours.size()]);
	}
	
	private String extractFecha(String item, int mes, int anio) {
		Matcher matcher = this.dayPattern.matcher(item);

		if (matcher.find()) {
			String dia = matcher.group();
			return new StringBuffer(dia.substring(0, dia.length() - 2)).append("/").append(mes).append("/")
					.append(anio).toString();
		} else {
			return null;
		}
	}

	private void addResults(List<Result> results, String nombreEvento,
			String nombreArtista, String fecha, String[] horarios) throws Exception{

		if (nombreArtista == null || nombreEvento == null || !isCorrectDate(fecha))
			return;

		if (horarios != null) {
			for (String horario : horarios) {
				results.add(new Result(nombreEvento, Arrays
						.asList(nombreArtista), fecha, horario, LUGAR,
						DIRECCION, BARRIO, WEB, MAIL, TEL, this.getSiteName()));
			}
		} else {
			results.add(new Result(nombreEvento, Arrays.asList(nombreArtista),
					fecha, SheSpider.NOT_FOUND, LUGAR, DIRECCION, BARRIO, WEB,
					MAIL, TEL, this.getSiteName()));
		}
	}

	private boolean isCorrectDate(String date) throws Exception{
		//Le sumo y resto un dia en milisegundos. Necesito hacer esto para la comparacion porque la fecha que parseo tiene como hora la medianoche.
		//Start y end date tienen como hora la hora en que se corre el chorizo, esto va a fallar si se corre justo a la medianoche (en el milisegundo exacto).
		
		if(date != null)
		{
			long currentDateInMillis = this.dateFormater.parse(date).getTime() + 86400000;		
			return currentDateInMillis  < endDate.getTimeInMillis() && currentDateInMillis > startDate.getTimeInMillis();
		}
		else
		{
			return false;
		}
	}
	
	private boolean isNewResult(String line) {
		Matcher matcher = this.firstLinePattern.matcher(line);
		return matcher.find();
	}

	/**
	 * Calcula el anio de los resultados que figuran en cafe vinilo. Calcula el
	 * anio a partir del mes que extrajo de la pagina y lo compara con el mes de
	 * la fecha de inicio y fecha de fin del crawleo del sitio. Si el mes no
	 * corresponde a ninguna fecha, devuelve 0 y ese mes no debe procesarse.
	 */
	private int calculateAnio(int mesActual, Calendar startDate,
			Calendar endDate) {
		if ((startDate.get(Calendar.MONTH) + 1) == mesActual) {
			return startDate.get(Calendar.YEAR);
		}
		if ((endDate.get(Calendar.MONTH) + 1) == mesActual) {
			return endDate.get(Calendar.YEAR);
		}
		return 0;
	}

	private String generateUrl() {
		return this.domain;
	}

	@Override
	protected String getSiteName() {
		return "Cafe Vinilo";
	}

}
